﻿using System;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;

namespace VisualView.Axis
{
    /// <summary>
    /// 轴基类
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public abstract class BaseAxis<T> : DrawingVisualView
    {
        public BaseAxis()
        {
            base.Visual = new DrawingVisual();
        }

        /// <summary>
        /// 轴类型
        /// </summary>
        public AxisType AxisType
        {
            get { return (AxisType)GetValue(AxisTypeProperty); }
            set { SetValue(AxisTypeProperty, value); }
        }
        public static readonly DependencyProperty AxisTypeProperty = DependencyProperty.Register("AxisType", typeof(AxisType), typeof(BaseAxis<T>), new PropertyMetadata(AxisType.X, PropertyChangedNeedRefresh));

        #region 样式
        /// <summary>
        /// 背景色
        /// </summary>
        public Brush Background
        {
            get { return (Brush)GetValue(BackgroundProperty); }
            set { SetValue(BackgroundProperty, value); }
        }
        public static readonly DependencyProperty BackgroundProperty = DependencyProperty.Register("Background", typeof(Brush), typeof(BaseAxis<T>), new PropertyMetadata(Brushes.Transparent, PropertyChangedNeedRefresh));

        protected static Typeface DefaultTypeface = new Typeface("Segoe UI");
        /// <summary>
        /// 字体样式
        /// </summary>
        public Typeface Typeface
        {
            get { return (Typeface)GetValue(TypefaceProperty); }
            set { SetValue(TypefaceProperty, value); }
        }
        public static readonly DependencyProperty TypefaceProperty = DependencyProperty.Register("Typeface", typeof(Typeface), typeof(BaseAxis<T>), new PropertyMetadata(DefaultTypeface, PropertyChangedNeedRefresh));

        /// <summary>
        /// 文字大小
        /// </summary>
        public double FontSize
        {
            get { return (double)GetValue(FontSizeProperty); }
            set { SetValue(FontSizeProperty, value); }
        }
        public static readonly DependencyProperty FontSizeProperty = DependencyProperty.Register("FontSize", typeof(double), typeof(BaseAxis<T>), new PropertyMetadata(12d, PropertyChangedNeedRefresh));

        protected static Pen DefaultLinePen = new Pen(Brushes.Black, 1d);
        /// <summary>
        /// 线条笔刷
        /// </summary>
        public Pen LinePen
        {
            get { return (Pen)GetValue(LinePenProperty); }
            set { SetValue(LinePenProperty, value); }
        }
        public static readonly DependencyProperty LinePenProperty = DependencyProperty.Register("LinePen", typeof(Pen), typeof(BaseAxis<T>), new PropertyMetadata(DefaultLinePen, PropertyChangedNeedRefresh));

        protected static Pen DefaultTextPen = new Pen(Brushes.Black, 0.1d);
        /// <summary>
        /// 文本笔刷
        /// </summary>
        public Pen TextPen
        {
            get { return (Pen)GetValue(TextPenProperty); }
            set { SetValue(TextPenProperty, value); }
        }
        public static readonly DependencyProperty TextPenProperty = DependencyProperty.Register("TextPen", typeof(Pen), typeof(BaseAxis<T>), new PropertyMetadata(DefaultTextPen, PropertyChangedNeedRefresh));
        #endregion

        #region 刷新和辅助方法
        protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo)
        {
            base.OnRenderSizeChanged(sizeInfo);
            Refresh();
            RangeChangedCommand?.Execute(this);
            RangeChangedEvent?.Invoke(this);
        }

        protected static Point PointZero = new Point(0d, 0d);
        /// <summary>
        /// 刷新轴，重新绘制Visual
        /// </summary>
        public abstract void Refresh();
        /// <summary>
        /// 属性发生变化需要重新绘制
        /// </summary>
        /// <param name="d"></param>
        /// <param name="e"></param>
        protected static void PropertyChangedNeedRefresh(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (d is BaseAxis<T> axis) axis.Refresh();
        }

        /// <summary>
        /// 创建文本的矢量数据
        /// </summary>
        /// <param name="str">文本</param>
        /// <param name="fontSize">文字大小</param>
        /// <param name="width">返回文本长度</param>
        /// <returns></returns>
        public Geometry CreateTextGeometry(string str, double fontSize, out double width, out double height)
        {
            var temp = new FormattedText(str, System.Globalization.CultureInfo.CurrentUICulture, FlowDirection.LeftToRight, Typeface, fontSize, TextPen.Brush, VisualTreeHelper.GetDpi(this).PixelsPerDip);
            width = temp.Width;
            height = temp.Height;
            return temp.BuildGeometry(PointZero);
        }

        /// <summary>
        /// 绘制轴长线
        /// </summary>
        /// <param name="dc"></param>
        protected virtual void DrawingAxisLine(DrawingContext dc)
        {
            if (AxisType == AxisType.X) dc.DrawLine(LinePen, new Point(0, 0), new Point(this.ActualWidth, 0));
            else if (AxisType == AxisType.X2) dc.DrawLine(LinePen, new Point(0, this.ActualHeight), new Point(this.ActualWidth, this.ActualHeight));
            else if (AxisType == AxisType.Y) dc.DrawLine(LinePen, new Point(this.ActualWidth, 0), new Point(this.ActualWidth, this.ActualHeight));
            else dc.DrawLine(LinePen, new Point(0, 0), new Point(0, this.ActualHeight));
        }

        /// <summary>
        /// 获取轴长度
        /// </summary>
        /// <returns></returns>
        public double GetLength()
        {
            return (AxisType == AxisType.X || AxisType == AxisType.X2) ? this.ActualWidth : this.ActualHeight;
        }

        /// <summary>
        /// 轴分段整数位步伐
        /// </summary>
        /// <param name="difference"></param>
        /// <param name="count"></param>
        /// <returns></returns>
        protected virtual double GetPace(double difference, int count)
        {
            var temp = Math.Pow(10, Math.Floor(Math.Log(difference / count) / Math.Log(10)));
            var residual = difference / count / temp;
            if (residual > 5) temp = 10 * temp;
            else if (residual > 2) temp = 5 * temp;
            else if (residual > 1) temp = 2 * temp;
            return temp;
        }
        #endregion

        #region 数据和位置转换方法
        /// <summary>
        /// 获取实际值、坐标值转换像素值参数
        /// </summary>
        /// <returns></returns>
        public virtual AxisValueLocationConvertParam GetValueLocationParam()
        {
            return new AxisValueLocationConvertParam(this.GetLength(), AxisType);
        }
        public Func<AxisValueLocationConvertParam> GetValueLocationParamFunc { get { return GetValueLocationParam; } }

        /// <summary>
        /// 获取值的像素位置
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public virtual double GetValueLocation(T value)
        {
            if (!IsLoaded) return default;
            return GetValueLocation(value, GetValueLocationParam());
        }
        public abstract double GetValueLocation(T value, AxisValueLocationConvertParam param);
        public Func<T, AxisValueLocationConvertParam, double> GetValueLocationFunc { get { return GetValueLocation; } }

        /// <summary>
        /// 获取像素为对应的值
        /// </summary>
        /// <param name="location"></param>
        /// <returns></returns>
        public virtual T GetLocationValue(double location)
        {
            if (!IsLoaded) return default;
            return GetLocationValue(location, GetValueLocationParam());
        }
        public abstract T GetLocationValue(double location, AxisValueLocationConvertParam param);
        public Func<double, AxisValueLocationConvertParam, T> GetLocationValueFunc { get { return GetLocationValue; } }
        #endregion

        #region 鼠标控制
        #region 移动轴范围
        /// <summary>
        /// 鼠标左键按下点位
        /// </summary>
        protected Point MouseLeftButtonDownPoint { get; set; }
        protected override void OnPreviewMouseLeftButtonDown(MouseButtonEventArgs e)
        {
            MouseLeftButtonDownPoint = e.GetPosition(this);
            base.OnPreviewMouseLeftButtonDown(e);
        }
        protected override void OnPreviewMouseLeftButtonUp(MouseButtonEventArgs e)
        {
            MouseLeftButtonDownPoint = default;
            base.OnPreviewMouseLeftButtonUp(e);
        }
        protected override void OnMouseLeave(MouseEventArgs e)
        {
            MouseLeftButtonDownPoint = default;
            base.OnMouseLeave(e);
        }
        protected override void OnPreviewMouseMove(MouseEventArgs e)
        {
            if (e.LeftButton == MouseButtonState.Pressed && MouseLeftButtonDownPoint != default)
            {
                var point = e.GetPosition(this);
                MoveRange(point.X - MouseLeftButtonDownPoint.X, MouseLeftButtonDownPoint.Y - point.Y);
                MouseLeftButtonDownPoint = point;
            }
            else base.OnPreviewMouseMove(e);
        }
        /// <summary>
        /// 移动轴范围
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        public virtual void MoveRange(double x, double y) { }
        #endregion

        #region 缩放范围
        protected override void OnPreviewMouseWheel(MouseWheelEventArgs e)
        {
            var point = e.GetPosition(this);
            ZoomRange(point, e.Delta > 0 ? 0.9d : 1.1d);
        }
        /// <summary>
        /// 缩放轴范围
        /// </summary>
        /// <param name="center">中心点位</param>
        /// <param name="zoom">缩放比例,大于1放大轴范围，小于1缩小轴范围</param>
        public virtual void ZoomRange(Point center, double zoom) { }
        #endregion
        #endregion

        #region 范围变化命令、事件
        /// <summary>
        /// 轴范围发生变化命令
        /// </summary>
        public ICommand RangeChangedCommand
        {
            get { return (ICommand)GetValue(RangeChangedCommandProperty); }
            set { SetValue(RangeChangedCommandProperty, value); }
        }
        public static readonly DependencyProperty RangeChangedCommandProperty = DependencyProperty.Register("RangeChangedCommand", typeof(ICommand), typeof(BaseAxis<T>), new PropertyMetadata(null));
        /// <summary>
        /// 轴范围发生变化事件
        /// </summary>
        public Action<BaseAxis<T>> RangeChangedEvent { get; set; }
        protected static void RangeChangedNeedRefresh(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (d is BaseAxis<T> axis)
            {
                axis.Refresh();
                axis.RangeChangedCommand?.Execute(axis);
                axis.RangeChangedEvent?.Invoke(axis);
            }
        }
        #endregion
    }

    /// <summary>
    /// 轴类型
    /// </summary>
    public enum AxisType
    {
        /// <summary>
        /// 底部x轴
        /// </summary>
        X = 0,
        /// <summary>
        /// 左侧y轴
        /// </summary>
        Y = 1,
        /// <summary>
        /// 顶部x轴
        /// </summary>
        X2 = 2,
        /// <summary>
        /// 右侧y轴
        /// </summary>
        Y2 = 3
    }

    public class AxisValueLocationConvertParam
    {
        /// <summary>
        /// 轴图形长度
        /// </summary>
        public double Length { get; protected set; }
        /// <summary>
        /// 轴类型
        /// </summary>
        public AxisType AxisType { get; protected set; }

        public AxisValueLocationConvertParam(double length, AxisType axisType)
        {
            this.Length = length;
            this.AxisType = axisType;
        }
    }
}
